#include <windows.h>
#include <assert.h>
#include <libObfuscateRedist/libObfuscate.h>
#include <openssl/evp.h>
#include <openssl/ripemd.h>
#include <openssl/applink.c>
#include <wincrypt.h>
#define __fcrypt__
#include <fcrypt.h>

HMODULE g_hlib = 0;
DWORD   g_tlsindex;
/**
 * Union holding LibObfuscate's static cipher types. Having them as 
 * local variables in the local scope trashes the stack inside 
 * SecureZeroMemory's compiler intrinsic on x64 as the stack incurs
 * a page fault when executing the atomic rep stosb.
 */
union LOB_STATIC_DATA
{
  RIJNDAEL_STATIC_DATA  rijndal;
  TWOFISH_STATIC_DATA   twofish;
  SERPENT_STATIC_DATA   serpent;
};
/**
 * CBC wrapper for LibObfuscate's ECB interfaces - Encryption. Encryption 
 * algorithm is picked based on algid switch. "length" is the length of the 
 * input buffer "key" is assumed to be 256 bit wide and "iv" is assumed to 
 * be block sized. These implementations currently work on a per algorithm 
 * basis, however it is pretty straightforward to instrument them as the 
 * primary 3 step cascade.(which currently happens in FC_Block_Encrypt/Decrypt)
 */
void
 lob_encrypt(const unsigned char *inBuf, unsigned char *outBuf, const unsigned char *key, const unsigned char *iv, int length, wchar_t algid)
{
  union LOB_STATIC_DATA lsd;
  int i;

  assert (!(length % 16));

  SecureZeroMemory(&lsd, sizeof (LOB_STATIC_DATA));

  if (algid == L'A') {
    lsd.rijndal.nrounds = Rijndael_set_key_encrypt(lsd.rijndal.key, key, 256);
  } else if (algid == L'T') {
    Twofish_set_key(&(lsd.twofish.key), (DWORD *)key , 256);
  } else if (algid == L'S') {
    Serpent_set_key(lsd.serpent.key, (DWORD *)key, 256);
  } else if (algid == L'N') {
    memmove(outBuf, inBuf, length);
    return;
  } else {
    assert(false);
  }
  /**
   * Run the loop "cipher block size" times (length/16) with the plain
   * text block at each iteration xor'ed with the cipher text block of
   * the previous iteration. First plain text block is xor'ed with the 
   * input iv.
   */
  for (i = 0; i < (length/16); i++)
  {
    unsigned char cbc[16] = { 0 };

    XOR16(inBuf + (i*16), iv, cbc);

    switch (algid)
    {
      case L'A':
        Rijndael_encrypt(lsd.rijndal.key, lsd.rijndal.nrounds, cbc, outBuf + (i*16));
        break;

      case L'T':
        Twofish_encrypt(&(lsd.twofish.key), (DWORD *)cbc, (DWORD *)((unsigned char *)outBuf + (i*16)));
        break;

      case L'S':
        Serpent_encrypt(lsd.serpent.key, (DWORD *)cbc, (DWORD *)((unsigned char *)outBuf + (i*16)));
        break;

      default:
        assert(0);
    }
    /**
     * Encrypted output block for this iteration becomes
     * the initialization vector for the next iteration.
     */
    iv = outBuf + (i*16);
  }
  SecureZeroMemory(&lsd, sizeof (LOB_STATIC_DATA));  
}
/**
 * CBC wrapper for LibObfuscate's AES ECB interfaces - Decryption.
 */
void
 lob_decrypt(const unsigned char *inBuf, unsigned char *outBuf, const unsigned char *key, const unsigned char *iv, int length, wchar_t algid)
{
  union LOB_STATIC_DATA lsd;
  int i;

  assert (!(length % 16));

  SecureZeroMemory(&lsd, sizeof (LOB_STATIC_DATA));

  if (algid == L'A') {
    lsd.rijndal.nrounds = Rijndael_set_key_decrypt(lsd.rijndal.key, key, 256);
  } else if (algid == L'T') {
    Twofish_set_key(&(lsd.twofish.key), (DWORD *)key , 256);
  } else if (algid == L'S') {
    Serpent_set_key(lsd.serpent.key, (DWORD *)key, 256);
  } else if (algid == L'N') {
    memmove(outBuf, inBuf, length);
    return;
  } else {
    assert(false);
  }
  /**
   * Run the loop "cipher block size" times i.e. length/16, with the result
   * of decryption at each iteration xor'ed with the cipher text block of the
   * previous iteration. First decrypted block is xor'ed with the input iv.
   */
  for (i = 0; i < (length/16); i++)
  {
    unsigned char cbc[16] = { 0 };

    switch (algid)
    {
      case L'A':
        Rijndael_decrypt(lsd.rijndal.key, lsd.rijndal.nrounds, inBuf + i*16, cbc);
        break;

      case L'T':
        Twofish_decrypt(&(lsd.twofish.key), (DWORD *)((unsigned char *)inBuf + i*16), (DWORD *)cbc);
        break;

      case L'S':
        Serpent_decrypt(lsd.serpent.key, (DWORD *)((unsigned char *)inBuf + i*16), (DWORD *)cbc);
        break;

      default:
        assert(0);
    }
    /**
     * XOR the decrypted block with the current iv and mark the cipher text for
     * this iteration as the initialization vector for the next iteration.
     */
    XOR16(cbc, iv, outBuf + (i*16));

    iv = inBuf + (i*16);
  }
  SecureZeroMemory(&lsd, sizeof (LOB_STATIC_DATA));
}
/**
 * Block XOR function for LibObfuscate CBC wrappers [out = e1 ^ e2]
 * TODO: have another, 2-step ULL version for X64.
 */
void
 XOR16(const unsigned char *e1, const unsigned char * e2, unsigned char *out)
{
  *((DWORD *)(out + 0))  = *((DWORD *)(e1 + 0))  ^  *((DWORD *)(e2 + 0));
  *((DWORD *)(out + 4))  = *((DWORD *)(e1 + 4))  ^  *((DWORD *)(e2 + 4));
  *((DWORD *)(out + 8))  = *((DWORD *)(e1 + 8))  ^  *((DWORD *)(e2 + 8));
  *((DWORD *)(out + 12)) = *((DWORD *)(e1 + 12)) ^  *((DWORD *)(e2 + 12));
}
/**
 * OpenSSL Encrypt.
 */
int
 oss_encrypt(const unsigned char *clear, int len_clear, const unsigned char *key, const unsigned char *iv, unsigned char *cipher) 
{
   EVP_CIPHER_CTX *ctx = NULL;
   int len, len_cipher, fRet;

   assert(!(len_clear %16));

   len = len_cipher = fRet = 0;

   /** Create and initialise the OpenSSL context */
   ctx = pEVP_CIPHER_CTX_new();

   if (!ctx) {
    goto _end;
   }
   /**
    * Initialise the encryption operation. IMPORTANT - ensure you use a key
    * and IV size appropriate for your cipher
    * In this example we are using 256 bit AES (i.e. a 256 bit key). The
    * IV size for *most* modes is the same as the block size. For AES this
    * is 128 bits
    */
   fRet = pEVP_EncryptInit_ex(ctx, pEVP_aes_256_cbc(), NULL, key, iv);

   if (fRet != 1) {
    goto _end;
   }
   /**
    * EVP_CIPHER_CTX_set_padding() enables or disables padding. By default encryption
    * operations are padded using standard block padding and the padding is checked and 
    * removed when decrypting. If the pad parameter is zero then no padding is performed,
    * the total amount of data encrypted or decrypted must then be a multiple of the 
    * block size or an error will occur.
    */
   fRet = pEVP_CIPHER_CTX_set_padding(ctx, 0);

   if (fRet != 1) {
    goto _end;
   }
   /**
    * Provide the message to be encrypted, and obtain the encrypted output.
    * EVP_EncryptUpdate can be called multiple times if necessary
    */
   fRet = pEVP_EncryptUpdate(ctx, cipher, &len, clear, len_clear);

   if (fRet != 1) {
    goto _end;
   }

   len_cipher = len;
   /**
    * Finalise the encryption. Further cipher text bytes may be written at
    * this stage.
    */
   fRet = pEVP_EncryptFinal_ex(ctx, cipher + len, &len);

   if (fRet != 1) {
    goto _end;
   }

   assert(len == 0);

   len_cipher += len;

   assert(len_cipher == len_clear);

   _end:
  
   if (fRet)
   /**
    * Instead of having test suits for the CBC wrapper 
    * (LibObfuscate's ECB interfaces) to test the official 
    * NIST tests vectors, we assert its correctness using 
    * OpenSSL as the reference implementation. You can
    * remove this scope.
    */
   {
    if (0) {
     unsigned char CT[512] = { 0 };
     unsigned char DT[512] = { 0 };
     /** Encrypt the original clear text using LibObfuscate CBC encryptor **/
     lob_encrypt(clear, CT, key, iv, len_cipher, 0);
     /** assert OpenSSL's encryption against LibObfuscate's encryption **/
     assert(!memcmp(CT, cipher, len_cipher));
     /** Decrypt OpenSLL generated cipher text using libobfuscate's CBC decryptor **/
     lob_decrypt(cipher, DT, key, iv, len_cipher, 0);
     /** assert LibObfuscate's decryption against the original clear text **/
     assert(!memcmp(DT, clear, len_cipher));
    }
   } else {
     /** Do something.. openSSL logging */
     len_cipher = 0;
   }

   /** Clean up */
   if (ctx) {
    pEVP_CIPHER_CTX_free(ctx);
   }

   return (len_cipher);
}

/**
 * OpenSSL Decrypt.
 */
int
 oss_decrypt(const unsigned char *cipher, int len_cipher, const unsigned char *key, const unsigned char *iv, unsigned char *clear)
{
  EVP_CIPHER_CTX *ctx = NULL;
  int len, len_clear, fRet;

  assert(!(len_cipher %16));

  len = len_clear = fRet = 0;
  /**
   * Create and initialise the cipher context.
   */
  ctx = pEVP_CIPHER_CTX_new();

  if (!ctx) {
    goto _end;
  }
  /**
   * Initialise the decryption operation. IMPORTANT - ensure you use a key
   * and IV size appropriate for your cipher. In this example we are using 
   * 256 bit AES (i.e. a 256 bit key). The iv size for *most* modes is the
   * same as the block size. For AES this is 128 bits.
   */
  fRet = pEVP_DecryptInit_ex(ctx, pEVP_aes_256_cbc(), NULL, key, iv);

  if (fRet != 1) {
    goto _end;
  }
  /**
   * EVP_CIPHER_CTX_set_pagdding() enables or disables padding. By default encryption
   * operations are padded using standard block padding and the padding is checked and 
   * removed when decrypting. If the pad parameter is zero then no padding is performed,
   * the total amount of data encrypted or decrypted must then be a multiple of the 
   * block size or an error will occur.
   */
  fRet = pEVP_CIPHER_CTX_set_padding(ctx, 0);

  if (fRet != 1) {
    goto _end;
  }
  /**
   * Provide the message to be decrypted, and obtain the plain text output.
   * EVP_DecryptUpdate can be called multiple times if necessary
   */
  fRet = pEVP_DecryptUpdate(ctx, clear, &len, cipher, len_cipher);

  if (fRet != 1) {
    goto _end;
  }

  len_clear = len;
  /**
   * Finalise the decryption. Further plain text bytes may be written at this stage.
   */
  fRet = pEVP_DecryptFinal_ex(ctx, clear + len, &len);

  if (fRet != 1) {
    goto _end;
  }

  assert(len == 0);

  len_clear += len;

  assert(len_clear == len_cipher);

  _end:

  /** Clean up */
  if (ctx) {
    pEVP_CIPHER_CTX_free(ctx);
  }

  return (len_clear);
}

int
 FC_Block_Decrypt(unsigned char *in, int len, unsigned char *out, wchar_t *szImage, long long ssn)
{
  int d_total = 0, i = 0;
  pDDK iterator = 0;

  iterator = (pDDK) TlsGetValue(g_tlsindex);

  assert(iterator);
  
  while(iterator && wcscmp(iterator->szImage, szImage)) {
    iterator = iterator->next;
  }

  if (!iterator || !(iterator->keys)) {
    return (0);
  }
  /**
   * Reverse decryption cascade decrypting each sector
   * using the 3 keys and the iv for this level.
   */
  unsigned char step1[512];
  unsigned char step2[512];

  /** CryptUnprotectMemory(iterator->keys, (256/8)*3, CRYPTPROTECTMEMORY_SAME_PROCESS); */

  /**
   * Generate this sector's iv using ESSIV, iv = E(salt, sector number) where salt = H(key)
   */
  SHA512_DATA sha;
  BYTE hash[64];
  /** generate a 512 bit hash of the master keys */
  Sha512_init(&sha);
  Sha512_data(&sha, iterator->keys, 32);
  Sha512_finalize(&sha , hash);

  for (i = 0; i < (len/512); i++)
  {
    /**
     * Use the first 256 bits of the key's hash to encrypt the sector number in ECB
     */
    RIJNDAEL_STATIC_DATA rijndael;
  
    BYTE snbuf[16] = { 0 }, iv[16] = { 0 };

    *((unsigned long long *)snbuf) = ssn++;
  
    memset(&rijndael, 0, sizeof(RIJNDAEL_STATIC_DATA));
    rijndael.nrounds = Rijndael_set_key_encrypt(rijndael.key, hash, 256);

    Rijndael_encrypt(rijndael.key, rijndael.nrounds, snbuf, iv);
    /**
     * 3 step sector decryption
     */
    lob_decrypt(in + (i*512), step1, iterator->keys + 32 + 32, iv, 512, iterator->cascade[2]);

    lob_decrypt(step1, step2, iterator->keys + 32, iv, 512, iterator->cascade[1]);

    lob_decrypt(step2, out + (i*512), iterator->keys, iv, 512, iterator->cascade[0]);
    /**
     * This also works fine ...
     * oss_decrypt(cipher + (i*512), 512, iterator->keys, iv, clear + (i*512)); 
     */
     d_total += 512;
  }

  /* CryptProtectMemory(iterator->keys, (256/8)*3, CRYPTPROTECTMEMORY_SAME_PROCESS); */

  return (d_total);
}

int
 FC_Block_Encrypt(unsigned char *in, int len, unsigned char *out, wchar_t *szImage, long long ssn)
{
   int e_total = 0, i = 0; 
   pDDK iterator = 0;

   iterator = (pDDK) TlsGetValue(g_tlsindex);

   assert(iterator);

   while (iterator && wcscmp(iterator->szImage, szImage)) {
     iterator = iterator->next;
   }

  if (!iterator || !(iterator->keys)) {
    return (0);
  }
  /**
   * 3 step encryption cascade, encrypting each sector using
   * 3 different, 256 bit keys and the iv for this level.
   */
  unsigned char step1[512];
  unsigned char step2[512];

  /** CryptUnprotectMemory(iterator->keys, (256/8)*3, CRYPTPROTECTMEMORY_SAME_PROCESS); */

  /**
   * Generate this sector's iv using ESSIV, iv = E(salt, sector number) where salt = H(key)
   */
  SHA512_DATA sha;
  BYTE hash[64];
  /** generate a 512 bit hash of the master keys */
  Sha512_init(&sha);
  Sha512_data(&sha, iterator->keys, 32);
  Sha512_finalize(&sha , hash);

  for (i = 0; i < (len / 512); i++)
  {
    /** 
     * Use the first 256 bits of the resulting hash to encrypt the sector number in ECB mode
     */
    RIJNDAEL_STATIC_DATA rijndael;

    BYTE snbuf[16] = { 0 }, iv[16] = { 0 };

    *((unsigned long long *)snbuf) = ssn++;

    memset(&rijndael, 0, sizeof(RIJNDAEL_STATIC_DATA));
    rijndael.nrounds = Rijndael_set_key_encrypt(rijndael.key, hash, 256);

    Rijndael_encrypt(rijndael.key, rijndael.nrounds, snbuf, iv);
    /**
     * 3 step sector encryption
     */
 
    /*oss_encrypt(in + (i*512), 512, iterator->keys, iv, step1); */

    lob_encrypt(in + (i*512), step1, iterator->keys, iv, 512, iterator->cascade[0]);

    lob_encrypt(step1, step2, iterator->keys + 32, iv, 512, iterator->cascade[1]);

    lob_encrypt(step2, out + (i*512), iterator->keys + 32 + 32, iv, 512, iterator->cascade[2]);

    e_total += 512;
  }

  /* CryptProtectMemory(iterator->keys, (256/8)*3, CRYPTPROTECTMEMORY_SAME_PROCESS); */

  return (e_total);
}
 /**
  * On input, "keys" contains the password supplied by the user
  * On output we direct PBKDF2 to generate a 768 bit key into it
  * using 70,000 iterations.
  */
unsigned char
 gen_keys(pDDK pDDkeys, unsigned char *salt, wchar_t cascade[], long iterations)
{
   unsigned char first[(256/8)*3] = { 0 };
   unsigned char last[(256/8)*3] = { 0 };
   unsigned char fRet = 0;
   int rc = 0;
   /**
    * For existing encrypted images which have a valid salt as
    * part of the first sector, salt would be a valid pointer. For
    * new images salt would be null.
    */
   if (!salt) {
     /** 
      * If the framework did not pass pointer to the persisted salt (which 
      * implies we are being called for a new image) generate 128 random bytes 
      * of salt for this image here... this slat would be used for key 
      * stretching and persisted in the first sector.
      */
     pRAND_bytes(pDDkeys->clear.salt, 128);

   } else {
     /**
      * Else, use what the framework passed us in *salt (which 
      * implies we are being called for an existing encrypted 
      * image)
      */
     memmove(pDDkeys->clear.salt, salt, 128);
   }
   /**
    * Transform the password quickly using a single iteration first.
    */
   /* CryptUnprotectMemory(pDDkeys->keys, (256/8)*3, CRYPTPROTECTMEMORY_SAME_PROCESS); */

   rc = pPKCS5_PBKDF2_HMAC(
         (const char *)pDDkeys->keys,
         wcslen((wchar_t*)pDDkeys->keys) * sizeof(wchar_t),
         pDDkeys->clear.salt,
         128,
         1,
         pEVP_sha512(),
         (256/8)*3,
         first);

   if (rc == 1) {
     /**
      * Zero out the password before executing remaining iterations. 
      */
     SecureZeroMemory(pDDkeys->keys, strlen((const char *)pDDkeys->keys));

     rc = pPKCS5_PBKDF2_HMAC(
            (const char *)first, (256/8)*3,
            pDDkeys->clear.salt,
            128,
            iterations - 1,
            pEVP_sha512(),
            (256/8)*3,
            last);

     if (rc == 1) {
       memmove(pDDkeys->keys, last, (256/8)*3);
       SecureZeroMemory(last, (256/8)*3);
       /* CryptProtectMemory(pDDkeys->keys, (256/8)*3, CRYPTPROTECTMEMORY_SAME_PROCESS); */
       pDDkeys->cascade[0] = cascade[0];
       pDDkeys->cascade[1] = cascade[1];
       pDDkeys->cascade[2] = cascade[2];
       fRet = 1;
     }
   }

   return (fRet);
}
/**
 * [K]ey [i]v and [s]alt [s]etup - KISS
 * FC_KISS is called by the framework after the user has entered the
 * credentials through UI's password dialogue. It is called both for 
 * new and existing images. salt is a pointer to [s]lat and [i]v for
 * an existing image and is null on input when this callback is being
 * called for a new image. For an already existing encrypted image the
 * framework reads the first sector and calls into FC_KISS with that.
 */
void *
 FC_KISS(wchar_t *szImage, unsigned char *salt, wchar_t *password, wchar_t cascade[], long iterations)
{
   unsigned char fRet = 0;
   pDDK g_head = 0;
   pDDK pDDKeys = 0;

   goto _start; /* This is to shut up the compiler */

   _start: /* and this is, in case you don't want to use the framework's password input mechanism */

   if (password && wcslen(password)) {

     pDDKeys = (pDDK) calloc(sizeof(DDK), 1);

     /** allocate space for 3, 256 bit keys; actual allocation would be a single page */
     pDDKeys->keys = (unsigned char *) VirtualAlloc(NULL, (256/8)*3, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
     /** lock the page in memory, this takes care of keys not ending up in the paging file */
     VirtualLock(pDDKeys->keys, (256/8)*3);
     /** Move the password queried from the UI dialogue into pDDKeys->keys */
     memmove(pDDKeys->keys, password, wcslen(password) * sizeof(wchar_t));
     /** put some signature bytes past the keys buffer so that we can run strings on the hyberfile */
     
     /** zero out the input password buffer */
     SecureZeroMemory(password, wcslen(password) * sizeof(wchar_t));

     /** CryptProtectMemory the DDKeys buffer 
     assert(!((256/8)*3 % CRYPTPROTECTMEMORY_BLOCK_SIZE));
     CryptProtectMemory(pDDKeys->keys, (256/8)*3, CRYPTPROTECTMEMORY_SAME_PROCESS); */
     /**
      * At this point you could unconditionally transform 
      * the input password here as per your plan...
      */
     fRet = gen_keys(pDDKeys, salt, cascade, iterations);

     if (fRet) {

      /** Image name would be used to select the correct key */
      /** [TODO: use something else] */
      pDDKeys->szImage = _wcsdup(szImage);
      /** Mark that the image is encrypted */
      pDDKeys->clear.flags[0] = 0x01;
      /**
       * Get the pointer to the head node for this thread from TLS
       */
      g_head = (pDDK) TlsGetValue(g_tlsindex);

      if (!g_head) {

        TlsSetValue(g_tlsindex, pDDKeys);

      } else {

        pDDK iterator = g_head;

        while (iterator->next) {
          iterator = iterator->next;
        }

        iterator->next = pDDKeys;
      }
     } /** if (fRet)  */
  } else {
    /** 
     * Password dialogue was cancelled without an input. This is a valid 
     * scope as you could very well cancel the framework's password prompt
     * and implement your own password input mechanism here followed by a 
     * jump to the _start label marked at the beginning of this function 
     * and it will all work just fine...
     */
    goto _end;
  }

  _end:

  if (!fRet) {
    if (pDDKeys) {
      if (pDDKeys->keys) {
        VirtualFree(pDDKeys->keys, 0, MEM_RELEASE), pDDKeys->keys = 0;
      }
      if (pDDKeys->szImage) {
        free(pDDKeys->szImage), pDDKeys->szImage = 0;
      }
      free(pDDKeys), pDDKeys = 0;
    }
  }

  return (&(pDDKeys->clear));
}

/**
 * The framework intercepts hibernation power event in primary thread's message pump and initiates 
 * a dismount on all active mounts. The dismount operation flushes the cache and terminates the mount. 
 * This callback is invoked per mount, after the dismount has happened and the keys are no longer needed. 
 * It zeros out the keys buffer for each differencing level so that they don't end up in the system's 
 * hibernation file. The system allows approximately two seconds for an application to handle this
 * notification. If an application is still performing operations after its time allotment has expired, 
 * the system may interrupt the application. Windows Server 2003 and Windows XP:  Applications are allowed 
 * up to 20 seconds to respond to the PBT_APMSUSPEND event.
 */
void
 FC_Destroy_Keys(void)
{
  pDDK iterator = 0;
  /**
   * Get the pointer to the head node for this thread from TLS
   */
  iterator = (pDDK) TlsGetValue(g_tlsindex);

  while (iterator) {
    SecureZeroMemory(iterator->keys, (256/8)*3);
    VirtualFree(iterator->keys, 0, MEM_RELEASE);
    iterator->keys = 0;
    iterator = iterator->next;
  }
}
unsigned char
 FC_Init(void)
{
  unsigned char fRet = 0;

  if (g_hlib) {
    return 1;
  }

  g_hlib = LoadLibrary("libeay32.dll");

  if (!g_hlib) {
    wprintf(L"\n Failed to load libeay32.dll. Error %d\n", GetLastError());
    return (fRet);
  }

  pOpenSSL_add_all_algorithms = (pfnOpenSSL_add_all_algorithms) GetProcAddress(g_hlib, "OPENSSL_add_all_algorithms_noconf");
  pEVP_CIPHER_CTX_set_padding = (pfnEVP_CIPHER_CTX_set_padding) GetProcAddress(g_hlib, "EVP_CIPHER_CTX_set_padding");    
  pERR_load_crypto_strings    = (pfnERR_load_crypto_strings) GetProcAddress(g_hlib, "ERR_load_crypto_strings");
  pEVP_EncryptFinal_ex        = (pfnEVP_EncryptFinal_ex) GetProcAddress(g_hlib, "EVP_EncryptFinal_ex");
  pERR_free_strings           = (pfnERR_free_strings) GetProcAddress(g_hlib, "ERR_free_strings");
  pEVP_DecryptUpdate          = (pfnEVP_DecryptUpdate) GetProcAddress(g_hlib, "EVP_DecryptUpdate");   
  pEVP_EncryptInit_ex         = (pfnEVP_EncryptInit_ex) GetProcAddress(g_hlib, "EVP_EncryptInit_ex");
  pEVP_EncryptUpdate          = (pfnEVP_EncryptUpdate) GetProcAddress(g_hlib, "EVP_EncryptUpdate");
  pEVP_DecryptFinal_ex        = (pfnEVP_DecryptFinal_ex) GetProcAddress(g_hlib, "EVP_DecryptFinal_ex");
  pEVP_CIPHER_CTX_new         = (pfnEVP_CIPHER_CTX_new) GetProcAddress(g_hlib, "EVP_CIPHER_CTX_new");
  pEVP_aes_256_cbc            = (pfnEVP_aes_256_cbc) GetProcAddress(g_hlib, "EVP_aes_256_cbc");   
  pEVP_CIPHER_CTX_free        = (pfnEVP_CIPHER_CTX_free) GetProcAddress(g_hlib, "EVP_CIPHER_CTX_free");
  pEVP_DecryptInit_ex         = (pfnEVP_DecryptInit_ex)  GetProcAddress(g_hlib, "EVP_DecryptInit_ex");   
  pOPENSSL_config             = (pfnOPENSSL_config) GetProcAddress(g_hlib, "OPENSSL_config");
  pEVP_cleanup                = (pfnEVP_cleanup) GetProcAddress(g_hlib, "EVP_cleanup");
  pRIPEMD160                  =  (pfnRIPEMD160) GetProcAddress(g_hlib, "RIPEMD160");
  pRIPEMD160_Init             = (pfnRIPEMD160_Init) GetProcAddress(g_hlib, "RIPEMD160_Init");
  pRIPEMD160_Update           = (pfnRIPEMD160_Update) GetProcAddress(g_hlib, "RIPEMD160_Update");
  pRIPEMD160_Final            = (pfnRIPEMD160_Final) GetProcAddress(g_hlib, "RIPEMD160_Final");
  pPKCS5_PBKDF2_HMAC          = (pfnPKCS5_PBKDF2_HMAC) GetProcAddress(g_hlib, "PKCS5_PBKDF2_HMAC");
  pEVP_ripemd160              = (pfnEVP_ripemd160) GetProcAddress(g_hlib, "EVP_ripemd160");
  pEVP_sha512                 = (pfnEVP_sha512) GetProcAddress(g_hlib, "EVP_sha512");
  pEVP_sha1                   = (pfnEVP_sha1) GetProcAddress(g_hlib, "EVP_sha1");   
  pRAND_bytes                 = (pfnRAND_bytes) GetProcAddress(g_hlib, "RAND_bytes");

  if (pOpenSSL_add_all_algorithms &&
      pEVP_CIPHER_CTX_set_padding &&
      pERR_load_crypto_strings    &&
      pEVP_EncryptFinal_ex        &&
      pEVP_DecryptFinal_ex        &&
      pEVP_CIPHER_CTX_free        &&
      pEVP_DecryptInit_ex         &&
      pEVP_EncryptInit_ex         &&
      pEVP_CIPHER_CTX_new         &&
      pEVP_DecryptUpdate          &&
      pEVP_EncryptUpdate          &&
      pPKCS5_PBKDF2_HMAC          &&
      pERR_free_strings           &&
      pRIPEMD160_Update           &&
      pRIPEMD160_Final            &&
      pEVP_aes_256_cbc            &&
      pOPENSSL_config             &&
      pRIPEMD160_Init             &&
      pEVP_ripemd160              &&
      pEVP_cleanup                &&
      pRAND_bytes                 &&
      pRIPEMD160                  &&
      pEVP_sha512                 )
  {
    fRet = 1;
  } else {
    wprintf(L"\n Required entry points not found in libeay32.dll\n");
    return (fRet);
  }
  /**
   * Initialise OpenSSL.
   */
  pERR_load_crypto_strings();

  pOpenSSL_add_all_algorithms();

  pOPENSSL_config(NULL);            

  return (fRet);
}

void
 FC_Finish(void)
{
  pDDK iterator;
  /** Clean up */
  pEVP_cleanup();
  pERR_free_strings();

  iterator = (pDDK) TlsGetValue(g_tlsindex);

  while (iterator) {
    
    pDDK tmp = iterator;
 
    if (iterator->keys) {
      SecureZeroMemory(iterator->keys, (256/8)*3);
      free(iterator->keys), iterator->keys = NULL;
    }
    if (iterator->szImage) {
      free(iterator->szImage), iterator->szImage = NULL;
     }
    iterator = iterator->next;
    free(tmp);
  }

  if (g_hlib) {
    FreeLibrary(g_hlib), g_hlib = NULL;
  }
}


unsigned char __stdcall
 DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
  hinstDLL;
  lpReserved;

  switch(fdwReason) 
  {
    case DLL_PROCESS_ATTACH:
    {
      if ((g_tlsindex = TlsAlloc()) == TLS_OUT_OF_INDEXES) 
        return FALSE;
    
      break;
    }
     case DLL_THREAD_ATTACH:
     {
      // Do thread-specific initialization.
       break;
     }
     case DLL_THREAD_DETACH:
       // Do thread-specific cleanup.
       break;

     case DLL_PROCESS_DETACH:
       // Perform any necessary cleanup.
       TlsFree(g_tlsindex);
        break;
  }
  return TRUE;
}

/**
 * AES Known Answer Test (KAT) Vectors http://csrc.nist.gov/groups/STM/cavp/
 *  COUNT = 0
 *  KEY = 0000000000000000000000000000000000000000000000000000000000000000
 *  IV = 00000000000000000000000000000000
 *  PLAINTEXT = 80000000000000000000000000000000
 *  CIPHERTEXT = ddc6bf790c15760d8d9aeb6f9a75fd4e
 */

#define BUF_SIZE 512

int
 main(void)
{
  unsigned char PT[BUF_SIZE]  = { 0x00 };  /* Plain text     */
  unsigned char CT[BUF_SIZE]  = { 0 };     /* Cipher text    */
  unsigned char DT[BUF_SIZE]  = { 0 };     /* Decrypted text */
  unsigned char key[32] = { 0 };
  unsigned char iv[16]  = { 0 };

  unsigned char e_lob[BUF_SIZE]  = { 0 };
  unsigned char e_oss[BUF_SIZE]  = { 0 };

  /** Initialise OpenSSL */
  if (!FC_Init()) {
    return 0;
  }
 
  pERR_load_crypto_strings();
  pOpenSSL_add_all_algorithms();
  pOPENSSL_config(NULL);
  /**
   * TEST1: assert lob CBC encryption and decryption wrappers are inverse functions.
   */  
  lob_encrypt(PT, CT, key, iv, BUF_SIZE, 0);

  lob_decrypt(CT, DT, key, iv, BUF_SIZE, 0);

  assert(!memcmp(PT, DT, BUF_SIZE));
  /** save the encryption result of lob CBC wrapper */
  memmove(e_lob, CT, BUF_SIZE);

  SecureZeroMemory(CT, BUF_SIZE);
  SecureZeroMemory(DT, BUF_SIZE);

  oss_encrypt(PT, BUF_SIZE, key, iv, CT);

  oss_decrypt(CT, BUF_SIZE, key, iv, DT);
  /**
   * assert inverse nature of OpenSSL, just in case....
   */
  assert(!memcmp(PT, DT, BUF_SIZE));
  /** save the encryption result of OpenSSL CBC */
  memmove(e_oss, CT, BUF_SIZE);

  SecureZeroMemory(CT, BUF_SIZE);
  SecureZeroMemory(DT, BUF_SIZE);
  /**
   * Test2: assert CBC wrapper encryption equals OpenSSL CBC encryption
   */
  assert(!memcmp(e_lob, e_oss, BUF_SIZE));
  /**
   * Test3 : assert lob is able to decrypt OpenSSL cipher text
   */
  lob_decrypt(e_oss, DT, key, iv, BUF_SIZE, 0);

  assert(!memcmp(DT, PT, BUF_SIZE));
  /**
   * Test4: assert 3 step encryption-decryption using lob wrapper
   */
   lob_encrypt(PT, CT, key, iv, BUF_SIZE, 0);
   lob_encrypt(CT, PT, key, iv, BUF_SIZE, 1);
   lob_encrypt(PT, CT, key, iv, BUF_SIZE, 2); 

   lob_decrypt(CT, PT, key, iv, BUF_SIZE, 2);
   lob_decrypt(PT, CT, key, iv, BUF_SIZE, 1);
   lob_decrypt(CT, PT, key, iv, BUF_SIZE, 0);

   unsigned char zero[BUF_SIZE] = { 0 };

   assert(!memcmp(zero, PT, BUF_SIZE));
  /** Clean up OpenSSL */
  pEVP_cleanup();
  pERR_free_strings();

  return 0;
}